package gbench.common.matlib.matrix;

import gbench.common.matlib.MatlibCanvas.*;
import gbench.common.matlib.rdd.BasePair;

import java.lang.reflect.Array;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static gbench.common.matlib.MatlibCanvas.NVec.mod;
import static gbench.common.matlib.rdd.BasePair.bp;
import static gbench.common.tree.LittleTree.kvp_int;

/**
 * 二阶向量 （抽象矩阵)
 *
 * @author gbench
 */
public abstract class XXVec<T, SELF> {

    /**
     * 二阶向量 （抽象矩阵)的构造函数
     *
     * @param data 矩阵数据
     */
    public XXVec(final XVec<XVec<T>> data) {
        this.data = data;
    }

    /**
     * 二阶向量 （抽象矩阵)的构造函数
     *
     * @param m  矩阵行数
     * @param n  矩阵列数
     * @param tt 矩阵数据
     */
    @SuppressWarnings("unchecked")
    public XXVec(int m, int n, final T... tt) {
        this.data = OF(m, n, tt);
    }

    /**
     * 提取元素数据
     *
     * @param i 行号索引从0开始
     * @param j 列号索引从0开始
     * @return (i, j) 所标识的元素数据
     */
    public T cell(Number i, Number j) {
        final var row = this.row(i);
        return row == null ? null : row.get(j.intValue());
    }

    /**
     * 提取源数据（行序列)
     * 
     * @return 源数据
     */
    public T[][] cells() {
        return this.toArray();
    }

    /**
     * 提取源数据（行序列) 数据扁平化
     * 
     * @return 源数据
     */
    public T[] flatA() {
        return this.flatX().toArray();
    }

    /**
     * 提取源数据（行序列) 并给予扁平化
     *
     * @return 扁平化数据序列
     */
    public XVec<T> flatX() {
        return this.flatX(true);
    }

    /**
     * 提取源数据并做扁平化处理
     * @param byrow 是否按行序展开,true 按行展开,false 按列序展开
     * @return 扁平化数据序列
     */
    @SuppressWarnings("unchecked")
    public XVec<T> flatX(final boolean byrow) {
        return byrow
                ? this.data.flatX((T) null)
                : ((XXVec<T, ?>) this.transpose()).flatX();
    }

    /**
     * 获取 第 i 行数据
     *
     * @param i 大于等于0的正整数,大于最大行数的整数会被按照行数进行取模
     * @return 第 i 行数据
     */
    public XVec<T> row(Number i) {
        int n = this.nrows();
        return this.data.get(i.intValue() % n);
    }

    /**
     * 获取 第 i 列数据
     *
     * @param i 大于等于0的正整数,大于最大列数的整数会被按照列数进行取模
     * @return 第 i 列数据
     */
    public XVec<T> col(Number i) {
        int n = this.ncols();
        return this.columns().get(i.intValue() % n);
    }

    /**
     * 提取行数据，
     * 
     * @param idxes 行索引集合 从0开始的剔除行号索引序列
     * @return 源数据
     */
    @SuppressWarnings("unchecked")
    public SELF rgets(final Number... idxes) {
        if (isEmpty())
            return (SELF) this;

        return this.duplicateXX(this.rows().gets(idxes));
    }

    /**
     * 提取行数据，
     *
     * @param idxes 行索引集合 从0开始的剔除行号索引序列
     * @return 源数据
     */
    @SuppressWarnings("unchecked")
    public SELF rgets2(final Number... idxes) {
        if (isEmpty())
            return (SELF) this;

        final var a = NVec.of(idxes);
        final var b = NVec.of(this.nrows(), i -> i);
        final var ii = b.diff(a);
        return this.duplicateXX(this.rows().gets(ii));
    }

    /**
     * 提取源数据（行序列)
     *
     * @param idxes 行索引集合 从0开始的剔除列号索引序列
     * @return 源数据
     */
    @SuppressWarnings("unchecked")
    public SELF cgets2(final Number... idxes) {
        if (isEmpty())
            return (SELF) this;

        final var a = NVec.of(idxes);
        final var b = NVec.of(this.ncols(), i -> i);
        final var ii = b.diff(a);
        return ((XXVec<T, SELF>) this.duplicateXX(this.columns().gets(ii))).transpose();
    }

    /**
     * 提取行数据，
     * 
     * @param idxes 行索引集合 从0开始的提取行号索引序列
     * @return 源数据
     */
    @SuppressWarnings("unchecked")
    public SELF cgets(final Number... idxes) {
        if (isEmpty())
            return (SELF) this;

        return ((XXVec<T, SELF>) this.duplicateXX(this.columns().gets(idxes))).transpose();
    }

    /**
     * 一阶余子式矩阵 X 表示XVec形式
     *
     * @param i 行号索引从0开始
     * @param j 列号索引从0开始
     * @return 余子式
     */
    @SuppressWarnings("unchecked")
    public SELF minorX(final Number i, final Number j) {
        return ((XXVec<T, SELF>) this.rgets2(i)).cgets2(j);
    }

    /**
     * 提取源数据（行序列)
     *
     * @return 源数据
     */
    public XVec<XVec<T>> data() {
        return this.data;
    }

    /**
     * 提取源数据（行序列), data() 行数的别名
     * 
     * @return 源数据
     */
    public XVec<XVec<T>> rows() {
        return this.data();
    }

    /**
     * 提取源数据（列序列)
     * 
     * @return 源数据
     */
    @SuppressWarnings("unchecked")
    public XVec<XVec<T>> columns() {
        return ((XXVec<T, Object>) this.transpose()).data;
    }

    /**
     * 构造一个复制品
     *
     * @param dd 矩阵数据
     * @return SELF 数据
     */
    public abstract SELF duplicate(final T[][] dd);

    /**
     * 构造一个复制品
     *
     * @param xvec 矩阵数据
     * @return SELF 数据
     */
    public SELF duplicate(final XVec<T> xvec) {
        return this.duplicate(xvec.reshape((T[][]) null, this.shape().intA()));
    }

    /**
     * 构造一个复制品
     *
     * @param xxvec 矩阵数据
     * @return SELF 数据
     */
    @SuppressWarnings("unchecked")
    public SELF duplicateXX(final XVec<XVec<T>> xxvec) {
        final var shape = xxvec.shape();

        if (shape.get(0) < 1 || shape.get(1) < 1) { // 创建一个空列表
            return this.duplicate((T[][]) Array.newInstance(this.flatA().getClass().getComponentType(), 0, 0));
        } else { // 创建成SELF对象
            return this.duplicate(xxvec.fmap(Seq::toArray).toArray());
        }
    }

    /**
     * 按照行进行数据拉链<br>
     * this 定义矩阵形状,决定结果矩阵行列数,结果矩阵的行列数与this的行列数相同，<br>
     * 即this为主矩阵，xxvec为从矩阵, 从矩阵的若有缺失数据,则采用循环补位的方式进行补填。<br>
     * 
     * @param <V>     另一个二阶向量的元素类型
     * @param <U>     结果向量的元素类型
     * @param xxvec   另一个二阶向量
     * @param mutator 变换器
     * @return U 类型的流
     */
    public <V, U> Stream<U> zipS(final XXVec<V, ?> xxvec, final BiFunction<T, V, U> mutator) {
        final var arows = this.rows(); // 第一位置矩阵 ：主位置
        final var brows = xxvec.rows(); // 第二位置矩阵：从位置
        return arows.zipS(brows).flatMap(p -> p._1().zipS(p._2())).map(p -> p.mutate(mutator));
    }

    /**
     * 按照行进行数据拉链 <br>
     * this 定义矩阵形状,决定结果矩阵行列数,结果矩阵的行列数与this的行列数相同，<br>
     * 即this为主矩阵，xxvec为从矩阵, 从矩阵的若有缺失数据,则采用循环补位的方式进行补填。<br>
     * 
     * @param <V>     另一个二阶向量的元素类型
     * @param <U>     结果向量的元素类型
     * @param xxvec   另一个二阶向量
     * @param mutator 变换器
     * @return XVec向量列表
     */
    public <V, U> XVec<U> zipX(final XXVec<V, ?> xxvec, final BiFunction<T, V, U> mutator) {
        return this.zipS(xxvec, mutator).collect(XVec.xveclc());
    }
    
    /**
     * 使用mapper转换集合data数据，生成u类型的数据流
     * 
     * @param <U>    结果元素类型
     * @param mapper 数据流映射函数,(i:从0开始,xvec:数据元素)->u
     * @return 生成u类型的数据流
     */
    public <U> XVec<U> mapX(final BiFunction<Long, XVec<T>, U> mapper) {
        return this.map(mapper).collect(XVec.xveclc());
    }

    /**
     * 使用mapper转换集合data数据，生成U类型的XVec
     * 
     * @param <U>    结果元素类型
     * @param mapper 数据流映射函数,(xvec:数据元素)->u
     * @return 生成U类型的XVec
     */
    public <U> XVec<U> mapX(final Function<XVec<T>, U> mapper) {
        return this.map(mapper).collect(XVec.xveclc());
    }

    /**
     * 使用mapper转换集合data数据，生成U类型的XVec
     * 
     * @param <U>    结果元素类型
     * @param mapper 数据流映射函数,(xvec:数据元素)->streamu
     * @return 生成U类型的XVec
     */
    public <U> XVec<U> flatMapX(final Function<XVec<T>, Stream<U>> mapper) {
        return this.flatMap(mapper).collect(XVec.xveclc());
    }

    /**
     * 使用mapper转换集合data数据，生成u类型的数据流
     * 
     * @param <U>    结果元素类型
     * @param mapper 数据流映射函数,(i:从0开始,xvec:数据元素)->streamu
     * @return 生成U类型的XVec
     */
    public <U> XVec<U> flatMapX(final BiFunction<Long, XVec<T>, Stream<U>> mapper) {
        return this.flatMap(mapper).collect(XVec.xveclc());
    }

    /**
     * 使用mapper转换集合data数据，生成U类型的XVec
     * 
     * @param <U>    结果元素类型
     * @param mapper 数据流映射函数,(i:从0开始,xvec:数据元素)->u
     * @return 生成U类型的XVec
     */
    public <U> Stream<U> map(final BiFunction<Long, XVec<T>, U> mapper) {
        return this.data.map(mapper);
    }

    /**
     * 使用mapper转换集合data数据，生成u类型的数据流
     * 
     * @param <U>    结果元素类型
     * @param mapper 数据流映射函数,(i:从0开始,t:数据元素)->u
     * @return 生成u类型的数据流
     */
    public <U> Stream<U> map(final Function<XVec<T>, U> mapper) {
        return this.data.map(mapper);
    }

    /**
     * 使用mapper转换集合data数据，生成u类型的数据流
     * 
     * @param <U>    结果元素类型
     * @param mapper 数据流映射函数,(xvec:数据元素)->streamu
     * @return 生成u类型的数据流
     */
    public <U> Stream<U> flatMap(final Function<XVec<T>, Stream<U>> mapper) {
        return this.data.flatMap(mapper);
    }

    /**
     * 使用mapper转换集合data数据，生成u类型的数据流
     * 
     * @param <U>    结果元素类型
     * @param mapper 数据流映射函数,(i:从0开始,xvec:数据元素)->streamu
     * @return 生成u类型的数据流
     */
    public <U> Stream<U> flatMap(final BiFunction<Long, XVec<T>, Stream<U>> mapper) {
        return this.data.flatMap(mapper);
    }

    /**
     * 先换药后换瓶 映射
     * 
     * @param <U>    元素类型
     * @param <X>    结果类型
     * @param mapper 数据流映射函数,(t:数据元素)->u
     * @param ctor   构造函数:xxvec->x
     * @return X类型的结果
     */
    public <U, X> X fmap(final Function<T, U> mapper, final Function<XVec<XVec<U>>, X> ctor) {
        return ctor.apply(this.data.fmap(xvec -> xvec.fmap(mapper)));
    }
    
    /**
     * 先换药后换瓶 映射
     * 
     * @param <U>    元素类型
     * @param <X>    结果类型
     * @param mapper 数据映射:(bp:(行索引:从0开始,列索引从0开始),t:数据元素)->u,
     * @param ctor   构造函数:xxvec->X
     * @return X类型的结果
     */
    public <U, X> X fmap(final BiFunction<BasePair<Long, Long>, T, U> mapper, final Function<XVec<XVec<U>>, X> ctor) {
        final var xxvec = this.data.fmap((i, xvec) -> xvec.fmap((j, e) -> mapper.apply(BasePair.bp(i, j), e)));
        return ctor.apply(xxvec);
    }

    /**
     * 矩阵转置
     *
     * @return 转置矩阵
     */
    @SuppressWarnings("unchecked")
    public SELF transpose() {

        final var shape = this.shape();
        final var m = shape.i4(0); // 行数
        final var n = shape.i4(1); // 列数
        final var ai = new AtomicInteger(); // 索引计数器
        final var dd = this.data
                .flatMap(e->e.stream())
                .map(Weightable.weightf(p -> ai.getAndIncrement() % n)) // 生成列号权重
                .sorted()
                .map(Weightable::value)
                .collect(XVec.xveclc())
                .reshape(n, m); // 按列号排序

        return this.duplicate((T[][]) dd);
    }

    /**
     * 矩阵转置 transpose 的别名
     *
     * @return 转置矩阵
     */
    public SELF tp() {
        return this.transpose();
    }

    /**
     * 矩阵转置 transpose 的别名
     *
     * @return 转置矩阵
     */
    public SELF T() {
        return this.transpose();
    }

    /**
     * 按照行顺序进行计算 (evalByRow的别名)
     * 
     * @param evaluator 行计算器 [[t]]->u
     * @param <U>       计算结果类型
     * @return U类型的计算结果
     */
    public <U> U eval(final Function<XVec<XVec<T>>, U> evaluator) {
        return this.evalByRow(evaluator);
    }

    /**
     * 按照行顺序进行计算
     * 
     * @param evaluatorR 行计算器 [[t]]->u
     * @param <U>        计算结果类型
     * @return U类型的计算结果
     */
    public <U> U evalByRow(final Function<XVec<XVec<T>>, U> evaluatorR) {
        return evaluatorR.apply(this.rows());
    }

    /**
     * 按照列顺序进行计算
     * 
     * @param evaluatorC 列计算器 [[t]]->u
     * @param <U>        计算结果类型
     * @return U类型的计算结果
     */
    public <U> U evalByCol(final Function<XVec<XVec<T>>, U> evaluatorC) {
        return evaluatorC.apply(this.rows());
    }

    /**
     * 判断矩阵是为空
     * 
     * @return 行列维度至少有一个为空
     */
    public boolean isEmpty() {
        final var shape = this.shape();
        return shape.i4(0) == 0 || shape.i4(1) == 0;
    }

    /**
     * 判断矩阵是否是方阵
     * 
     * @return 判断矩阵是否是方阵
     */
    public boolean isSquare() {
        final var shape = this.shape();
        return shape.i4(0).equals(shape.i4(1));
    }

    /**
     * 判断矩阵是否是单个元素的 1x1 矩阵,例如[[a]]
     * 
     * @return 判断矩阵是否是单个元素, true 1x1矩阵, false 非 1x1 矩阵
     */
    public boolean isOneXOne() {
        return this.isSquare() && this.nrows() == 1;
    }

    /**
     * 行数
     *
     * @return 行数
     */
    public int nrows() {
        return this.shape().i4(0);
    }

    /**
     * 列数
     *
     * @return 列数
     */
    public int ncols() {
        return this.shape().i4(1);
    }

    /**
     * 求出矩阵的形状
     *
     * @return 矩阵的形状 (行数,列数)
     */
    public NVec shape() {
        return this.data.shape().mutate(NVec::new);
    }

    /**
     * 稀疏矩阵(等长行)
     *
     * @param <U>    结果类型
     * @param mapper 元素函数 ((i,j),t)->u
     * @return 元素为U类型的流
     */
    public <U> Stream<U> dataStream(final Function<BasePair<BasePair<Integer, Integer>, T>, U> mapper) {
        return this.dataStream(mapper, true);
    }

    /**
     * 稀疏矩阵
     *
     * @param <U>         结果类型
     * @param mapper      元素函数 ((i,j),t)->u
     * @param homogeneous 数据行是否为同类等长。
     * @return 元素为U类型的流
     */
    public <U> Stream<U> dataStream(final Function<BasePair<BasePair<Integer, Integer>, T>, U> mapper,
            boolean homogeneous) {
        if (homogeneous) { // 行为等长
            final var n = this.ncols();
            final var ai = new AtomicInteger();// 计数器
            final var index = (Supplier<BasePair<Integer, Integer>>) () -> mod(ai.getAndIncrement(), n)
                    .fmap(Double::intValue).mutate(BasePair::bptt);
            return this.rows().flatMap(cols -> cols.stream(t -> bp(index.get(), t))).map(mapper);
        } else { // 行为不等长
            return this.rows().map(kvp_int())
                    .flatMap(pr -> pr._2().stream(kvp_int()).map(pc -> bp(bp(pr._1(), pc._1()), pc.value())))
                    .map(mapper);
        }
    }

    /**
     * 遍历矩阵元素
     * @param cons 元素回调函数 ((i,j),t)->{}
     * @return 矩阵的稀疏矩阵表示
     */
    public List<BasePair<BasePair<Integer, Integer>, T>> forEach(final BiConsumer<BasePair<Integer, Integer>, T> cons ){
        return this.dataStream(e->{
            cons.accept(e.key(), e.value());
            return e;
        }).collect(Collectors.toList());
    }

    /**
     * 转换成数组
     * 
     * @return 二维数组
     */
    public T[][] toArray() {
        return this.data.fmap(Seq::toArray).toArray();
    }

    /**
     * 字符串函数 <br>
     * data 的元素被视为行
     */
    public String toString() {
        return this.isEmpty() ? "[empty]" // 空列表
                : this.data
                    .fmap(xvec -> xvec.fmap(e -> e + "").collect(Collectors.joining("\t")))
                    .collect(Collectors.joining("\n")); // 矩阵化显示
    }

    /**
     * 数据矩阵数据
     *
     * @param <T> 元素类型
     * @param tt  数据矩阵数据
     * @return 数据矩阵数据
     */
    @SafeVarargs
    public static <T> XVec<XVec<T>> OF(final T[]... tt) {
        return XVec.OF(tt).fmap((Function<T[], XVec<T>>) XVec::OF);
    }

    /**
     * 数据矩阵数据
     *
     * @param <T> 元素类型
     * @param m   行数
     * @param n   列数
     * @param tt  数据矩阵
     * @return 数据矩阵数据
     */
    @SuppressWarnings("unchecked")
    public static <T> XVec<XVec<T>> OF(final int m, final int n, final T... tt) {
        return OF((T[][]) XVec.OF(tt).reshape(m, n));
    }

    /**
     * 生成复数矩阵(行顺序填满)
     * 
     * @param <T>       元素类型
     * @param m         行数
     * @param n         列数
     * @param generator 矩阵元素生成器,(i:行号索引从0开始,j:列号索引从0开始)->c
     * @return 复数矩阵
     */
    public static <T> XVec<XVec<T>> OF(final int m, final int n, final BiFunction<Integer, Integer, T> generator) {
        return Range.cph(m, n).map(idx -> generator.apply(idx[0], idx[1]))
                .collect(Seq.reshapeclc((T[][] tt) -> XVec.OF(tt).fmap((Function<T[], XVec<T>>) XVec::OF), m, n));
    }

    /**
     * 生成复数矩阵(行顺序填满) ,是 of 函数的别名。名称 来自于M行 乘(X) N列 的命名习惯
     * 
     * @param <T>       元素类型
     * @param m         行数
     * @param n         列数
     * @param generator 矩阵元素生成器,(i:行号索引从0开始,j:列号索引从0开始)->c
     * @return 复数矩阵
     */
    public static <T> XVec<XVec<T>> MXN(final int m, final int n, final BiFunction<Integer, Integer, T> generator) {
        return Range.cph(m, n).map(idx -> generator.apply(idx[0], idx[1]))
                .collect(Seq.reshapeclc((T[][] tt) -> XVec.OF(tt).fmap((Function<T[], XVec<T>>) XVec::OF), m, n));
    }

    private final XVec<XVec<T>> data; // 矩阵数据
}